package org.liveboardgames.agricola.domain;

import java.io.Serializable;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.liveboardgames.agricola.domain.board.BoardTile;
import org.liveboardgames.agricola.domain.card.ActionCard;
import org.liveboardgames.agricola.domain.card.Card;
import org.liveboardgames.agricola.domain.card.ImprovementCard;
import org.liveboardgames.agricola.domain.player.Player;
import org.liveboardgames.agricola.model.structure.Visitable;
import org.liveboardgames.agricola.model.structure.Visitor;

public class Game implements Serializable, Visitable {

  private Long id;

  private List<Player> players;

  private Set<Player> harvestFieldPlayers;

  private Set<Player> harvestFeedPlayers;

  private Set<Player> harvestBreedPlayers;

  private List<BoardTile> tiles;

  private Player currentPlayer;

  private Boolean hasPlayerPlayedPeep;

  private List<ActionCard> waitingActionCards;

  private Set<ImprovementCard> majorImprovements;
  
  private Boolean isFinished;

  /**
   * We do not want an external call to set the id of the game for us, but
   * rather let Hibernate handle it itself.
   * 
   * @param id
   *          the {@link #id} to set
   */
  public void setId(Long id) {
    this.id = id;
  }

  /**
   * @return the {@link #id}
   */
  public Long getId() {
    return id;
  }

  /**
   * @return the {@link #players}
   */
  public List<Player> getPlayers() {
    return players;
  }

  /**
   * @param players
   *          the {@link #players} to set
   */
  public void setPlayers(List<Player> players) {
    this.players = players;
  }

  /**
   * @return the {@link #harvestFieldPlayers}
   */
  public Set<Player> getHarvestFieldPlayers() {
    return harvestFieldPlayers;
  }

  /**
   * @param harvestFieldPlayers
   *          the {@link #harvestFieldPlayers} to set
   */
  public void setHarvestFieldPlayers(Set<Player> harvestFieldPlayers) {
    this.harvestFieldPlayers = harvestFieldPlayers;
  }

  /**
   * @return the {@link #harvestFeedPlayers}
   */
  public Set<Player> getHarvestFeedPlayers() {
    return harvestFeedPlayers;
  }

  /**
   * @param harvestFeedPlayers
   *          the {@link #harvestFeedPlayers} to set
   */
  public void setHarvestFeedPlayers(Set<Player> harvestFeedPlayers) {
    this.harvestFeedPlayers = harvestFeedPlayers;
  }

  /**
   * @return the {@link #harvestBreedPlayers}
   */
  public Set<Player> getHarvestBreedPlayers() {
    return harvestBreedPlayers;
  }

  /**
   * @param harvestBreedPlayers
   *          the {@link #harvestBreedPlayers} to set
   */
  public void setHarvestBreedPlayers(Set<Player> harvestBreedPlayers) {
    this.harvestBreedPlayers = harvestBreedPlayers;
  }

  public void addHarvestFeedPlayer(Player player) {
    if (this.harvestFeedPlayers == null) {
      this.harvestFeedPlayers = new HashSet<Player>();
    }
    this.harvestFeedPlayers.add(player);
  }

  public void addHarvestFieldPlayer(Player player) {
    if (this.harvestFieldPlayers == null) {
      this.harvestFieldPlayers = new HashSet<Player>();
    }
    this.harvestFieldPlayers.add(player);
  }

  public void addHarvestBreedPlayer(Player player) {
    if (this.harvestBreedPlayers == null) {
      this.harvestBreedPlayers = new HashSet<Player>();
    }
    this.harvestBreedPlayers.add(player);
  }

  /**
   * @return the {@link #tiles}
   */
  public List<BoardTile> getTiles() {
    return tiles;
  }

  /**
   * @param tiles
   *          the {@link #tiles} to set
   */
  public void setTiles(List<BoardTile> tiles) {
    this.tiles = tiles;
  }

  /**
   * @return the {@link #currentPlayer}
   */
  public Player getCurrentPlayer() {
    return currentPlayer;
  }

  /**
   * @param currentPlayer
   *          the {@link #currentPlayer} to set
   */
  public void setCurrentPlayer(Player currentPlayer) {
    this.currentPlayer = currentPlayer;
  }

  /**
   * @return the {@link #hasPlayerPlayedPeep}
   */
  public Boolean getHasPlayerPlayedPeep() {
    return hasPlayerPlayedPeep;
  }

  /**
   * @param hasPlayerPlayedPeep
   *          the {@link #hasPlayerPlayedPeep} to set
   */
  public void setHasPlayerPlayedPeep(Boolean hasPlayerPlayedPeep) {
    this.hasPlayerPlayedPeep = hasPlayerPlayedPeep;
  }

  /**
   * @return the {@link #waitingActionCards}
   */
  public List<ActionCard> getWaitingActionCards() {
    return waitingActionCards;
  }

  /**
   * @param waitingActionCards
   *          the {@link #waitingActionCards} to set
   */
  public void setWaitingActionCards(List<ActionCard> waitingActionCards) {
    this.waitingActionCards = waitingActionCards;
  }

  /**
   * @return the {@link #majorImprovements}
   */
  public Set<ImprovementCard> getMajorImprovements() {
    return majorImprovements;
  }

  /**
   * @param majorImprovements
   *          the {@link #majorImprovements} to set
   */
  public void setMajorImprovements(Set<ImprovementCard> majorImprovements) {
    this.majorImprovements = majorImprovements;
  }

  public void setIsFinished(Boolean isFinished) {
    this.isFinished = isFinished;
  }

  public Boolean getIsFinished() {
    return isFinished;
  }

  public void acceptVisit(Visitor visitor) {
    visitor.visit(this);

    if (this.players != null) {
      for (Player player : this.players) {
        player.acceptVisit(visitor);
      }
    }

    if (this.majorImprovements != null) {
      for (Card card : this.majorImprovements) {
        card.acceptVisit(visitor);
      }
    }

    if (this.tiles != null) {
      for (BoardTile boardTile : this.tiles) {
        boardTile.acceptVisit(visitor);
      }
    }

    if (this.waitingActionCards != null) {
      for (Card card : this.waitingActionCards) {
        card.acceptVisit(visitor);
      }
    }
  }

  @Override
  public boolean equals(Object obj) {
    return this.id == null ? false : (obj instanceof Game && this.id.equals(((Game)obj).id));
  }

  @Override
  public int hashCode() {
    return this.id == null ? 0 : this.id.intValue();
  }
}
